package com.stud.common.utils;

import com.google.common.collect.Lists;
import com.stud.common.constant.CycleConstants;
import com.stud.common.core.domain.Cycle;
import com.stud.common.core.domain.CycleParams;
import com.stud.common.enums.AxisEnum;
import com.stud.common.enums.CycleEnum;

import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.List;
import java.util.Objects;

/**
 * @author Stud
 * @title XXXXX
 * @desription XXXXX
 * @data 2022-09-27 9:39
 */
public class CycleUtil {
    public static Cycle cycle(CycleEnum cycleEnum, CycleParams cycleParams) {
        return cycle(cycleEnum, LocalDateTime.now(), cycleParams);
    }

    public static Cycle cycle(CycleEnum cycleEnum, LocalDateTime datetime) {
        return cycle(cycleEnum, datetime, CycleConstants.DEFAULT_CYCLE_PARAMS);
    }

    public static Cycle cycle(CycleEnum cycleEnum) {
        return cycle(cycleEnum, LocalDateTime.now(), CycleConstants.DEFAULT_CYCLE_PARAMS);
    }

    public static Cycle cycle(CycleEnum cycleEnum, LocalDateTime datetime, CycleParams cycleParams) {
        if (cycleEnum == null) {
            throw new RuntimeException("cycleEnum不能为空");
        }
        Cycle cycle = new Cycle();
        switch (cycleEnum) {
            case HOUR24:
                cycle.setStart(datetime.plusHours(-24).withMinute(0).withSecond(0).withNano(0));
                cycle.setEnd(datetime);
                break;
            case HOUR48:
                cycle.setStart(datetime.plusHours(-48).withMinute(0).withSecond(0).withNano(0));
                cycle.setEnd(datetime);
                break;
            case HOUR72:
                cycle.setStart(datetime.plusHours(-72).withMinute(0).withSecond(0).withNano(0));
                cycle.setEnd(datetime);
                break;
            case DAY7:
                cycle.setStart(LocalDateTime.of(datetime.plusDays(-7).toLocalDate(), CycleConstants.LOCAL_TIME_MIN));
                cycle.setEnd(LocalDateTime.of(datetime.toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                break;
            case DAY15:
                cycle.setStart(LocalDateTime.of(datetime.plusDays(-15).toLocalDate(), CycleConstants.LOCAL_TIME_MIN));
                cycle.setEnd(LocalDateTime.of(datetime.toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                break;
            case DAY30:
                cycle.setStart(LocalDateTime.of(datetime.plusDays(-30).toLocalDate(), CycleConstants.LOCAL_TIME_MIN));
                cycle.setEnd(LocalDateTime.of(datetime.toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                break;
            case YESTERDAY:
                datetime = datetime.plusDays(-1);
                cycle.setStart(LocalDateTime.of(datetime.toLocalDate(), CycleConstants.LOCAL_TIME_MIN));
                cycle.setEnd(LocalDateTime.of(datetime.toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                break;
            case DAY:
                cycle.setStart(LocalDateTime.of(datetime.toLocalDate(), CycleConstants.LOCAL_TIME_MIN));
                cycle.setEnd(LocalDateTime.of(datetime.toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                break;
            case WEEK:
                datetime = datetime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
                cycle.setStart(LocalDateTime.of(datetime.toLocalDate(), CycleConstants.LOCAL_TIME_MIN));
                cycle.setEnd(LocalDateTime.of(cycle.getStart().plusDays(6).toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                break;
            case MONTH: //由于存在 月开始日,月结束日 配置,所以这里需要设置time,用于业务数据归月
                if (Objects.isNull(cycleParams.getMonthStartDay())
                        || Objects.equals(cycleParams.getMonthStartDay(), 1)
                        || Objects.equals(cycleParams.getMonthStartDay(), 0)) {
                    datetime = datetime.with(TemporalAdjusters.firstDayOfMonth());
                    cycle.setStart(LocalDateTime.of(datetime.toLocalDate(), CycleConstants.LOCAL_TIME_MIN));
                    cycle.setEnd(LocalDateTime.of(datetime.with(TemporalAdjusters.lastDayOfMonth()).toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                    cycle.setTime(datetime.format(CycleConstants.PATTERN_MONTH));
                } else {
                    LocalDateTime start;
                    int monthStartDay = Math.abs(cycleParams.getMonthStartDay());
                    if (datetime.getDayOfMonth() < monthStartDay) {
                        start = datetime.plusMonths(-1).withDayOfMonth(monthStartDay);
                    } else {
                        start = datetime.withDayOfMonth(monthStartDay);
                    }
                    if (cycleParams.getMonthStartDay() < 0) {
                        cycle.setStart(LocalDateTime.of(start.toLocalDate(), CycleConstants.LOCAL_TIME_MIN));
                        cycle.setEnd(LocalDateTime.of(start.plusMonths(1).plusDays(-1).toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                        cycle.setTime(cycle.getEnd().format(CycleConstants.PATTERN_MONTH));
                    } else {
                        cycle.setStart(LocalDateTime.of(start.toLocalDate(), CycleConstants.LOCAL_TIME_MIN));
                        cycle.setEnd(LocalDateTime.of(start.plusMonths(1).plusDays(-1).toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                        cycle.setTime(cycle.getStart().format(CycleConstants.PATTERN_MONTH));
                    }
                }
                break;
            case QUARTER:
                datetime = LocalDateTime.now().with(TemporalAdjusters.firstDayOfMonth());
                int quarter = getQuarter(datetime);
                int startMonth = quarter * 3 - 2;
                LocalDateTime quarterStart = datetime.plusMonths(startMonth - datetime.getMonthValue());
                LocalDateTime quarterEnd = quarterStart.plusMonths(3).plusDays(-1);
                cycle.setStart(LocalDateTime.of(quarterStart.toLocalDate(), CycleConstants.LOCAL_TIME_MIN));
                cycle.setEnd(LocalDateTime.of(quarterEnd.toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                break;
            case YEAR:
                datetime = datetime.with(TemporalAdjusters.firstDayOfYear());
                cycle.setStart(LocalDateTime.of(datetime.toLocalDate(), CycleConstants.LOCAL_TIME_MIN));
                cycle.setEnd(LocalDateTime.of(datetime.with(TemporalAdjusters.lastDayOfYear()).toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                break;
            default:
                throw new RuntimeException("不支持该周期类型");
        }
        return cycle;
    }

    public static List<Cycle> cycleList(CycleEnum cycleEnum, AxisEnum axisEnum, CycleParams cycleParams) {
        return cycleList(cycle(cycleEnum), axisEnum, cycleParams);
    }

    public static List<Cycle> cycleList(CycleEnum cycleEnum, AxisEnum axisEnum) {
        return cycleList(cycle(cycleEnum), axisEnum, CycleConstants.DEFAULT_CYCLE_PARAMS);
    }

    public static List<Cycle> cycleList(Cycle cycle, AxisEnum axisEnum) {
        return cycleList(cycle, axisEnum, CycleConstants.DEFAULT_CYCLE_PARAMS);
    }

    /**
     * @param cycle
     * @param axisEnum
     * @return
     */
    public static List<Cycle> cycleList(Cycle cycle, AxisEnum axisEnum, CycleParams cycleParams) {
        if (cycleParams.getIncrement() <= 0) {
            throw new RuntimeException("增量最小为1");
        }

        DateTimeFormatter dateTimeFormatter = Objects.isNull(cycleParams.getDateTimeFormatter()) ? axisEnum.getDateTimeFormatter() : cycleParams.getDateTimeFormatter();
        LocalDateTime date;
        List<Cycle> list = Lists.newArrayList();
        switch (axisEnum) {
            case MINUTE:
                date = cycle.getStart();
                while (!date.isAfter(cycle.getEnd())) {
                    list.add(new Cycle(date.format(dateTimeFormatter)));
                    date = date.plusMinutes(cycleParams.getIncrement());
                }
                break;
            case HOUR:
                date = cycle.getStart();
                while (!date.isAfter(cycle.getEnd())) {
                    list.add(new Cycle(date.format(dateTimeFormatter)));
                    date = date.plusHours(cycleParams.getIncrement());
                }
                break;
            case DAY:
                date = LocalDateTime.of(cycle.getStart().toLocalDate(), CycleConstants.LOCAL_TIME_MIN);
                while (!date.isAfter(cycle.getEnd())) {
                    list.add(new Cycle(date.format(dateTimeFormatter)));
                    date = date.plusDays(cycleParams.getIncrement());
                }
                break;
            case WEEK:
                date = LocalDateTime.of(cycle.getStart().toLocalDate(), CycleConstants.LOCAL_TIME_MIN);
                while (!date.isAfter(cycle.getEnd())) {
                    int year = date.get(WeekFields.ISO.weekBasedYear());
                    int week = date.get(WeekFields.ISO.weekOfWeekBasedYear());
                    String time = String.format("%d-%d", year, week);
                    list.add(new Cycle(time));
                    date = date.plusWeeks(cycleParams.getIncrement());
                }
                break;
            case MONTH://由于存在 月开始日,月结束日 配置,所以这里需要设置开始结束时间,用于业务数据归月
                date = LocalDateTime.of(cycle.getStart().toLocalDate(), CycleConstants.LOCAL_TIME_MIN);
                if (Objects.isNull(cycleParams.getMonthStartDay())
                        || Objects.equals(cycleParams.getMonthStartDay(), 1)
                        || Objects.equals(cycleParams.getMonthStartDay(), 0)) {
                    while (!date.isAfter(cycle.getEnd())) {
                        Cycle tempCycle = new Cycle();
                        LocalDateTime start = date.with(TemporalAdjusters.firstDayOfMonth());
                        LocalDateTime end = date.with(TemporalAdjusters.lastDayOfMonth());
                        tempCycle.setStart(start);
                        tempCycle.setEnd(LocalDateTime.of(end.toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                        tempCycle.setTime(date.format(dateTimeFormatter));
                        list.add(tempCycle);
                        date = date.plusMonths(cycleParams.getIncrement());
                    }
                } else {
                    int monthStartDay = Math.abs(cycleParams.getMonthStartDay());
                    if (date.getDayOfMonth() < monthStartDay) {
                        date = date.plusMonths(-1).withDayOfMonth(monthStartDay);
                    } else {
                        date = date.withDayOfMonth(monthStartDay);
                    }
                    if (cycleParams.getMonthStartDay() < 0) {
                        while (!date.isAfter(cycle.getEnd())) {
                            Cycle tempCycle = new Cycle();
                            LocalDateTime end = date.plusMonths(1).plusDays(-1);
                            tempCycle.setStart(date);
                            tempCycle.setEnd(LocalDateTime.of(end.toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                            tempCycle.setTime(end.format(dateTimeFormatter));
                            list.add(tempCycle);
                            date = date.plusMonths(cycleParams.getIncrement());
                        }
                    } else {
                        while (!date.isAfter(cycle.getEnd())) {
                            Cycle tempCycle = new Cycle();
                            LocalDateTime end = date.plusMonths(1).plusDays(-1);
                            tempCycle.setStart(date);
                            tempCycle.setEnd(LocalDateTime.of(end.toLocalDate(), CycleConstants.LOCAL_TIME_MAX));
                            tempCycle.setTime(date.format(dateTimeFormatter));
                            list.add(tempCycle);
                            date = date.plusMonths(cycleParams.getIncrement());
                        }
                    }
                }

                break;
            case QUARTER:
                date = LocalDateTime.of(cycle.getStart().toLocalDate(), CycleConstants.LOCAL_TIME_MIN);
                while (!date.isAfter(cycle.getEnd())) {
                    list.add(new Cycle(String.format("%d-%d", date.getYear(), getQuarter(date))));
                    date = date.plusMonths(3L * cycleParams.getIncrement());
                }
                break;
            case YEAR:
                date = LocalDateTime.of(cycle.getStart().toLocalDate(), CycleConstants.LOCAL_TIME_MIN);
                while (!date.isAfter(cycle.getEnd())) {
                    list.add(new Cycle(date.format(dateTimeFormatter)));
                    date = date.plusYears(cycleParams.getIncrement());
                }
                break;
            default:
                throw new RuntimeException("不支持该X轴类型");
        }

        return list;
    }

    public static int getQuarter(LocalDateTime dateTime) {
        return (dateTime.getMonthValue() - 1) / 3 + 1;
    }

    public static void main(String[] args) {
//        System.out.println("HOUR24==" + cycle(CycleEnum.HOUR24, LocalDateTime.now().plusDays(-50)));
//        System.out.println("HOUR48==" + cycle(CycleEnum.HOUR48, LocalDateTime.now().plusDays(-50)));
//        System.out.println("HOUR72==" + cycle(CycleEnum.HOUR72, LocalDateTime.now().plusDays(-50)));
//        System.out.println("DAY7==" + cycle(CycleEnum.DAY7, LocalDateTime.now().plusDays(-50)));
//        System.out.println("DAY15==" + cycle(CycleEnum.DAY15, LocalDateTime.now().plusDays(-50)));
//        System.out.println("DAY30==" + cycle(CycleEnum.DAY30, LocalDateTime.now().plusDays(-50)));
//        System.out.println("DAY==" + cycle(CycleEnum.DAY, LocalDateTime.now().plusDays(-50)));
//        System.out.println("WEEK==" + cycle(CycleEnum.WEEK, LocalDateTime.now().plusDays(-50)));

//        CycleParams cycleParams = new CycleParams();
//        cycleParams.setMonthStartDay(-25);
//        System.out.println("MONTH==" + cycle(CycleEnum.MONTH, LocalDateTime.of(2023, 11, 25, 0, 0), cycleParams));

//        System.out.println("QUARTER==" + cycle(CycleEnum.QUARTER));
//        System.out.println("YEAR==" + cycle(CycleEnum.YEAR));


        CycleParams cycleParams = new CycleParams();
        cycleParams.setMonthStartDay(3);
        List<Cycle> cycleList = cycleList(CycleEnum.YEAR, AxisEnum.MONTH, cycleParams);
        cycleList.forEach(cycle -> {
            System.out.println(cycle.getStart() + "====" + cycle.getEnd());
        });
    }
}
